/*************************************************************************************************************
 * 文件名:		MODEBUS_RTU.c
 * 功能:		MODEBUS_RTU通信协议层
 * 作者:		cp1300@139.com
 * 创建时间:	2014-03-24
 * 最后修改时间:2014-11-17
 * 详细:		MODEBUS RTU通信协议层
*************************************************************************************************************/
#ifndef _MODEBUS_RTU_H_
#define _MODEBUS_RTU_H_
 
//#include "system.h"
//#include "ucos_ii.h"
#include "FreeRTOS.h"
#include "task.h"
#include "main.h"
#include "cmsis_os.h"

typedef int bool;
#define TRUE (!0);
#define FALSE 0;
 
/***********************配置相关************************/
#define MODEBUS_RTU_HOST		1			//1:开启主机模式;0:关闭主机模式
#define MODEBUS_RTU_SLAVE		1			//1:开启从机模式;0:关闭从机模式
/*********************************************************/
 

 
//16位整形数高低对调
#define SWAP16(x)   (((x & 0xff00) >> 8) | ((x & 0xff) << 8))
 
 
 
/***********************关接口函数************************/
/**********************移植需要修改***********************/
#define MODEBUS_UartInit(ch,Speed)	 				UARTx_Init((UART_CH_Type)ch, Speed, ENABLE)					//串口初始化
#define MODEBUS_GetDataCnt(ch)						UARTx_GetRxCnt((UART_CH_Type)ch)							//获取接收数据计数器
#define MODEBUS_ClearRxCnt(ch)						UARTx_ClearRxCnt((UART_CH_Type)ch)							//清除接收数据计数器
#define MODEBUS_GetDataOver(ch)						UARTx_GetRxBuffFullFlag((UART_CH_Type)ch)					//获取数据溢出标志

//#define MODEBUS_SendData(ch,pbuff,len) 				UARTx_SendData((UART_CH_Type)ch, pbuff, len)				//数据发送
#define MODEBUS_SetRxBuff(ch, RxBuff, RxBuffSize) 	UARTx_SetRxBuff((UART_CH_Type)ch, RxBuff, RxBuffSize)		//设置串口接收缓冲区
#define MODEBUS_DisableRx(ch)						(UARTx_EnableRx((UART_CH_Type)ch, DISABLE))					//串口接收关闭
#define MODEBUS_EnableRx(ch)						(UARTx_EnableRx((UART_CH_Type)ch, ENABLE))					//串口接收使能
#define MODEBUS_SetBaudRate(ch, x)					(UARTx_SetBaudRate((UART_CH_Type)ch, x))					//设置串口波特率
//系统延时函数,根据实际修改,如果使用ucos建议使用ucos系统延时
#define MODEBUS_Delay10MS()							OSTimeDlyHMSM(0,0,0,10)										//10ms延时,字节超时固定为10ms	

void MODEBUS_SendData(USART_TypeDef *USARTx,uint8_t *buff,  uint8_t lens);
/*********************************************************/
 
 
//支持的功能码
#define MRTU_FUN_READ_HOLD		0x03			//读保持寄存器,可读写寄存器为保持寄存器
#define MRTU_FUN_READ_INPUT		0x04			//读输入寄存器,为只读寄存器
#define MRTU_FUN_WRITE			0x06			//写单个保持寄存器
#define MRTU_FUN_MWRITE			0x10			//写多个保持寄存器

#define MRTU_FUN_WRITE_ADD			0x11			//写从设备地址
 
//大端数据标记
#define BIG_U16		uint16_t							//16位整形数,需要转换为大端模式,兼容modubus
#define u16		uint16_t							//16位整形数,需要转换为大端模式,兼容modubus
#define u32		uint32_t							//16位整形数,需要转换为大端模式,兼容modubus 

#define u8 uint8_t
//读取寄存器类型选择
typedef enum
{
	HOLD_REG 	= 	MRTU_FUN_READ_HOLD,			//保持寄存器
	INPUT_REG	=	MRTU_FUN_READ_INPUT,		//输入寄存器
} READ_REG_TYPE;
 
 
//数据读取 主机数据帧,主机读取从机的数据帧
typedef __packed struct
{
	uint8_t	addr;				//地址 address
	uint8_t	fun;				//功能码 function
	BIG_U16	StartReg;		//数据起始地址
	BIG_U16	RegNum;			//需要读取的寄存器个数
	BIG_U16	CRC16;			//CRC16
} MRTU_READ_FRAME;			//MODEBUS RTU master Read Reg Frame
 
 
 
//预置单个保持寄存器,主机写从机单个寄存器的数据帧
//从机返回数据帧与主机预置单个寄存器数据帧一样
typedef __packed struct
{
	uint8_t	addr;				//地址 address
	uint8_t	fun;				//功能码 function
	BIG_U16	StartReg;		//数据起始地址
	BIG_U16	RegData;		//数据值
	BIG_U16 crc16;			//CRC校验值
} MRTU_WRITE_FRAME;			//MODEBUS RTU master Write Reg Frame
 
 
 
 
 
//预置多个保持寄存器,主机写从机多个寄存器的数据帧
typedef __packed struct
{
	uint8_t	addr;				//地址 address
	uint8_t	fun;				//功能码 function
	BIG_U16	StartReg;		//数据起始地址
	BIG_U16	RegNum;			//寄存器数量
	uint8_t	DataLen;			//数据长度
	uint8_t	DataBuff[2];		//寄存器的值	
} MRTU_WRITE_MULT_FRAME;			
 
 
//预置多个保持寄存器后返回数据帧,从机返回主机的数据帧
typedef __packed struct
{
	uint8_t	addr;				//地址 address
	uint8_t	fun;				//功能码 function
	BIG_U16	StartReg;		//数据起始地址
	BIG_U16	RegNum;			//寄存器数量
	BIG_U16 crc16;			//CRC校验值
} MRTU_WRIT_EMULT_RFRAME;			
 
 
//读取从机返回数据帧格式,从机返回给主机的数据帧
typedef __packed struct
{
	uint8_t	addr;				//地址 address
	uint8_t	fun;				//功能码 function
	uint8_t	DataLen;			//数据长度
	uint8_t	DataBuff[2];		//数据区,CRC16放在最后结尾处
	//MRTU_REG16	CRC16;	//CRC16
} MRTU_RETURN_FRAME;	//MODEBUS RTU master Read Reg Frame
 
 
//从机返回的异常数据帧,从机返回的异常数据帧
typedef __packed struct
{
	uint8_t	addr;				//地址 address
	uint8_t	ErrorFun;			//错误功能码 function+0x80
	uint8_t	unu;				//异常码
	uint8_t	crc16H;				//CRC16放在最后结尾处
	uint8_t	crc16L;				//CRC16放在最后结尾处
} MRTU_UNU_FRAME;	
 
 
//从机数据包解析后的相关信息
typedef struct
{
	uint8_t	SlaveAddr;	//主机发送的从机地址
	uint8_t 	RegNum;		//主机需要读取从机的寄存器数量
	uint8_t	fun;		//主机发送给从机的功能码
	u16 StartReg;	//主机需要读写的从机寄存器地址
} MRTU_SLAVE_INFO;
 
 
//异常码定义
typedef enum
{
	MRTU_UNUS1		=	0x01,	//异常码1,无效的操作码
	MRTU_UNUS2		=	0x02,	//异常码2,无效的数据地址
	MRTU_UNUS3		=	0x03,	//异常码3,无效的数据值
	MRTU_UNUS4		=	0x04,	//异常码4,无效操作
	MRTU_UNUS5		=	0x05,	//异常码5
	MRTU_UNUS6		=	0x06,	//异常码6
} MRTU_UNUS;
 
 
//错误状态
typedef enum
{
	MRTU_OK 				= 	0,		//OK
	MRTU_TIME_OUT 			= 	1,		//超时
	MRTU_OVER_ERROR 		= 	2,		//溢出
	MRTU_CRC_ERROR			=	3,		//CRC错误
	MRTU_ADDR_ERROR			=	4,		//地址错误,返回地址不一致
	MRTU_REG_ERROR			=	5,		//寄存器地址错误,返回寄存器地址不一致
	MRTU_FUNR_ERROR			=	6,		//功能码错误,返回功能码不一致或者不支持的功能码
	MRTU_HANDLE_ERROR		=	7,		//句柄错误,句柄为空
	MRTU_REGN_ERROR			=	8,		//寄存器数量错误
	MRTU_LEN_ERROR			=	9,		//返回数据长度错误
	MRTU_WRITE_ERROR		=	10,		//写寄存器错误,写入与读取不一致
	MRTU_UNUS1_ERROR		=	0x81,	//异常码1,无效的操作码
	MRTU_UNUS2_ERROR		=	0x82,	//异常码2,无效的数据地址
	MRTU_UNUS3_ERROR		=	0x83,	//异常码3,无效的数据值
	MRTU_UNUS4_ERROR		=	0x84,	//异常码4,无效操作
	MRTU_UNUS5_ERROR		=	0x85,	//异常码5
	MRTU_UNUS6_ERROR		=	0x86,	//异常码6
	MRTU_OTHER_ERROR = 0xff
} MRTU_ERROR;
 
 
 
 
//MODEBUS句柄结构
typedef struct
{
	uint8_t	UartCh;			//串口通道
	u32 BaudRate;		//通信波特率
	uint8_t *pRxBuff;		//接收缓冲区
	uint8_t *pTxBuff;		//发送缓冲区
	u32 RxBuffSize;		//接收缓冲区大小
	u32	TxPacketNum;	//发送数据包计数
	u32 RxPacketNum;	//接收数据包计数
	u32 ErrorNum;		//通信错误计数
	u16 TimeOut;		//通信超时时间,单位ms
	u16 ReturnTime;		//数据返回时间,单位ms,只对于主机有效
} MODEBUS_HANDLE;
 
 
 
bool MODEBUS_slave_Init(MODEBUS_HANDLE *pHandle, uint8_t UartCh, u32 BaudRate, uint8_t *pRxBuff,uint8_t *pTxBuff, u32 RxBuffSize, u32 TimeOut);					//初始化modebus
bool MODEBUS_host_Init(MODEBUS_HANDLE *pHandle, uint8_t UartCh, u32 BaudRate, uint8_t *pRxBuff,uint8_t *pTxBuff, u32 RxBuffSize, u32 TimeOut);					//初始化modebus
#if(MODEBUS_RTU_HOST) //开启主机模式
MRTU_ERROR MODEBUS_HOST_ReadReg(MODEBUS_HANDLE *pHandle, READ_REG_TYPE RegType, uint8_t SlaveAddr, u16 RegAddr, u16 *pRegData);					//主机读取从机指定单个寄存器
MRTU_ERROR MODEBUS_HOST_ReadMultReg(MODEBUS_HANDLE *pHandle, READ_REG_TYPE RegType, uint8_t SlaveAddr, u16 RegAddr, uint8_t RegNum, u16 pRegData[]);	//主机读取从机多个指定寄存器
MRTU_ERROR MODEBUS_HOST_WriteReg(MODEBUS_HANDLE *pHandle,uint8_t SlaveAddr, u16 RegAddr, u16 RegData);											//主机写从机单个保持寄存器
MRTU_ERROR MODEBUS_HOST_WriteMultReg(MODEBUS_HANDLE *pHandle,uint8_t SlaveAddr, u16 RegAddr, uint8_t RegNum, u16 pRegData[]);							//主机写从机单个保持寄存器
#endif
 
#if(MODEBUS_RTU_SLAVE) //开启从机模式
bool MODEBUS_SLAVE_RetrunUnu(MODEBUS_HANDLE *pHandle,uint8_t SlaveAddr, uint8_t Fun, MRTU_UNUS Unus);													//从机返回异常码
MRTU_ERROR MODEBUS_SLAVE_FramesUnpack(MODEBUS_HANDLE *pHandle,uint8_t SlaveAddr, u32 DataLen, uint8_t *pFun);											//从机解析数据包
MRTU_ERROR MODEBUS_SLAVE_ReturnReadReg(MODEBUS_HANDLE *pHandle, uint8_t Fun, uint8_t SlaveAddr, u16 RegAddr, uint8_t RegNum, u16 pRegData[]);				//从机返回主机读取的寄存器
MRTU_ERROR MODEBUS_SLAVE_ReturnWriteHoldReg(MODEBUS_HANDLE *pHandle,uint8_t SlaveAddr, u16 RegAddr, u16 RegData);								//从机返回主机写入单个寄存器命令
MRTU_ERROR MODEBUS_SLAVE_ReturnWrite_add(MODEBUS_HANDLE *pHandle,uint8_t SlaveAddr, u16 RegAddr, u16 RegData);	//写从地址
MRTU_ERROR MODEBUS_SLAVE_ReturnWriteMultHoldReg(MODEBUS_HANDLE *pHandle, uint8_t SlaveAddr, u16 RegAddr, uint8_t RegNum);								//从机返回主机写多个寄存器命令
void MODEBUS_SLAVE_ReadUnpack(MODEBUS_HANDLE *pHandle, MRTU_SLAVE_INFO *pFrameInfo);														//从机解析主机读取命令
void MODEBUS_SLAVE_WriteUnpack(MODEBUS_HANDLE *pHandle, MRTU_SLAVE_INFO *pFrameInfo, u16 *pData);											//从机解析主机写单个寄存器命令
void MODEBUS_SLAVE_WriteMultUnpack(MODEBUS_HANDLE *pHandle, MRTU_SLAVE_INFO *pFrameInfo, u16 *pDataBuff);									//从机解析主机多个寄存器命令
#endif
 
 
u16 usMBCRC16( uint8_t * pucFrame, u16 usLen );								//crc计算
void MODEBUS_32TO16(u16 *Out16H, u16 *Out16L, u32 In32);				//将32bit数据拆分为高低16位,并且使用大端模式,兼容modebus
u32 MODEBUS_16TO32(u16 In16H, u16 In16L);								//将modebus高低16位转换为小端模式的32位数
	
#endif /*_MODEBUS_RTU_H_*/